//RK

#include "UInt.hpp"
#include <iostream>
#include <stdlib.h>
#include <threads/threadPool.hpp>
#include <threads/MixTask.hpp>
#include <boost/foreach.hpp>
#include <algorithm>
#include <vector>
#include <algorithm>

ui::UInt::UInt()
{

}
namespace
{
int SaveProfiles(const std::vector<dna::profile>& pros, std::string outnam)
{
	size_t i = 0;
	std::for_each(pros.begin(), pros.end(),
			[&i, &outnam](const dna::profile& profile)
			{
				std::string ext = ".txt";
				std::string p = "_";
				std::string ai = std::to_string(i++ + 1);
				std::string filen = outnam + p + ai + ext;
				std::ofstream outputFile(filen.c_str());
				std::for_each(profile.begin(), profile.end(), [&outputFile](const dna::genotype& genotype)
						{
							outputFile << genotype.first << " " << genotype.second << "\n";
						}
				);
			});
	return 0;
}

class FileOutput: public generator::ProfileOutput
{
public:
	FileOutput(std::string name) :
			i_(0), name_(name)
	{
	}
	virtual void put(const std::vector<dna::profile>& profile)
	{
		std::string sout = std::to_string(i_++);
		sout += name_;
		SaveProfiles(profile, sout);
	}
private:
	int i_;
	std::string name_;
};

}  // namespace

ui::UInt::~UInt()
{

}

int ui::UInt::StartInterface(std::string mix, int howman, std::string out,
		int threads)
{
	howManyPeople = howman;
	std::vector<dna::marker> mixture;
	//parsed profiles
	readmixture::ReadMixture rm;
	try
	{
		std::cout << mix << "\n";
		std::cout << mix << "***\n";
		mixture = rm.parseMixFromFile(mix, howman);
		std::cout << "size" << mixture.size() << "\n";
	} catch (exc::FOpenException *e)
	{

		std::cout << "Nie ma takiego pliku z mieszaniną\n";
	} catch (exc::SizeException *e)
	{
		std::cout << "Za duzo alleli w mieszaninie\n";
	}
	UnifyAndMix(mixture, threads, generator::outputPtr(new FileOutput(out)));

	return 0;
}

int ui::UInt::StartInterface(std::string mix, std::string pro1, std::string out,
		int threads, int howmanypeople)
{
	howManyPeople = howmanypeople;
	std::vector<dna::marker> mixture;
	//parsed profiles
	readmixture::ReadMixture rm;
	dna::profile p1;
	try
	{
		std::cout << mix << "\n";
		std::cout << mix << "***\n";
		mixture = rm.parseMixFromFile(mix, howManyPeople);
		std::cout << "size" << mixture.size() << "\n";
	}
	catch (exc::FOpenException *e)
	{

		std::cout << "Nie ma takiego pliku z mieszaniną\n";
		return 1;
	}
	catch (exc::SizeException *e)
	{
		std::cout << "Za duzo alleli w mieszaninie\n";
		return 1;
	}

	try
	{
		p1 = rm.parseProFromFile(pro1);

	}
	catch (exc::FOpenException *e)
	{
		std::cout
				<< "Jeden z podanych plików z profilami nie istnieje- proszę podać ponownie\n";
		return 1;

	}
	if (rm.checkProfileInMixture(mixture, p1))
	{
		if (checkCoverage(mixture, p1, howManyPeople) == false)
		{
			std::cout
					<< "BLAD! profil ma za mało alleli do pokrycia mieszaniny !\n";
			return 1;
		}
		UnifyMixRemove(mixture, p1, threads,
				generator::outputPtr(new FileOutput(out)));
	}
	else
	{
		std::cout << "Jeden z profili nie moze wystapic w mieszaninie\n";
		return 1;
	}

	return 0;
}

int ui::UInt::StartInterface()
{
	//Information about how many people 
	//are in the mix 
	howManyPeople = 0;
	//parsed mixture	
	std::vector<dna::marker> mixture;
	//parsed profiles
	std::vector<dna::profile> profiles;
	readmixture::ReadMixture rm;
	std::string filename;

	std::cout << "Projekt MBI: mieszaniny DNA \n" << "Semestr 13Z\n"
			<< "Malwina Kowalczyk\n" << "Rafał Krawczyk\n"
			<< "Dawid Wysakowicz\n\n";
	while (1)
	{
		int option = 0;
		dna::profile p;
		std::string mixname;
		std::string inputd;
		std::cout << "Prosze wybrac jedna z opcji:\n"
				<< "1.Wczytanie mieszaniny markerow z pliku\n"
				<< "2.Wczytanie profilu z pliku\n"
				<< "3.Generowanie profili genetycznych z mieszaniny\n"
				<< "4.Wygenerowanie profilu na podstawie mieszaniny oraz danych profili\n"
				<< "5.Informacja o wczytanym profilu oraz o wczytanej mieszaninie\n"
				<< "6.Zakończenie programu\n\n";
		std::cin >> option;
		switch (option)
		{
		case 1:
			std::cout
					<< "Prosze podac ile ludzi jest w mieszaninie (2 lub 3):\n";

			std::cin >> inputd;
			if (inputd != "2" && inputd != "3")
			{
				std::cout << "Blad- dopuszczalne wartosci to 2 lub 3:\n";
				break;
			}
			std::cout << "Prosze podac nazwe pliku z mieszanina:\n";
			std::cin >> mixname;
			try
			{
				mixture = rm.parseMixFromFile(mixname, atoi(inputd.c_str()));
			} catch (exc::FOpenException *e)
			{
				std::cout << "Nie ma takiego pliku\n";
			} catch (exc::SizeException *e)
			{
				std::cout << "Za duzo alleli w mieszaninie\n";
			}
			howManyPeople = atoi(inputd.c_str());
			std::cout << "Parsowanie mieszaniny zakończone powodzeniem\n";
			std::cout << "Usuwanie starych profili\n";
			profiles.clear();
			break;
		case 2:
			if (mixture.empty())
			{
				std::cout << "Blad- konieczne wstepne wszytanie mieszaniny\n";
				break;
			}
			if (!profiles.empty())
			{
				std::cout << "Usuwanie starego profilu profili\n";
				profiles.clear();
			}

			std::cout << "Prosze podac nazwe pliku profilem:\n";
			std::cin >> mixname;
			try
			{

				p = rm.parseProFromFile(mixname);

			} catch (exc::FOpenException *e)
			{
				std::cout << "Nie ma takiego pliku- proszę podać ponownie\n";
			}
			if (rm.checkProfileInMixture(mixture, p))
			{
				profiles.push_back(p);
				if (checkCoverage(mixture, profiles[0], howManyPeople) == false)
				{
					std::cout
							<< "BLAD! profil ma za mało alleli do pokrycia mieszaniny !\n";
					profiles.clear();
				}
			}
			else
			{
				std::cout << "Taki profil nie moze wystapic w mieszaninie\n";
			}

			break;
		case 3:
			std::cout << "Podaj nazwe rdzenia pliku wynikowego\n";
			if (mixture.empty())
			{
				std::cout << "Nie wczytano jeszcze mieszaniny\n";
				break;
			}

			std::cin >> filename;
			UnifyAndMix(mixture, 1,
					generator::outputPtr(new FileOutput(filename)));
			break;
		case 4:
			if (mixture.empty() || profiles.empty())
			{
				std::cout << "Nie wczytano jeszcze profilu lub mieszaniny\n";
				break;
			}
			std::cout << "Podaj nazwe rdzenia pliku wynikowego\n";
			std::cin >> filename;
			UnifyMixRemove(mixture, profiles[0], 1,
					generator::outputPtr(new FileOutput(filename)));
			break;

		case 5:
		{
			std::cout << "Mieszanina-markery:\n";
			for (unsigned int i = 0; i != mixture.size(); i++)
				std::cout << mixture[i] << "\n";
			std::cout << "Profil:\n";
			size_t i = 0;
			std::for_each(profiles.begin(), profiles.end(),
					[&i](dna::profile& profile)
					{
						std::cout<<i++ +1<<":*****\n";
						std::for_each(profile.begin(), profile.end(), [](dna::genotype& genotype)
								{
									std::cout<<genotype<<"\n";
								});
					});
			std::cout << "*****\n";
		}
			break;
		case 6:
			std::cout << "Do widzenia !\n";
			return 0;
		default:
			std::cout << "Bledne polecenie\n";
		}

	}
}

bool ui::UInt::UnifyAndMix(const std::vector<dna::marker>& mixture, int threads,
		std::shared_ptr<generator::ProfileOutput> stream)
{
	std::list<std::list<dna::genotypeSet>> markers;
	std::for_each(mixture.begin(), mixture.end(), [&markers, &threads, this](const dna::marker& marker){
		std::list<dna::genotypeSet> unifiedAndMixedMarker;
		UnifyAndMixMarker(marker, threads, unifiedAndMixedMarker);
		markers.push_back(unifiedAndMixedMarker);
	});
	generator::ProfileGenerator pg;
	pg.generateAll(markers, stream, howManyPeople);

	return true;
}

bool ui::UInt::UnifyMixRemove(const std::vector<dna::marker>& mixture,
		const dna::profile& profileToRemove, int threads,
		std::shared_ptr<generator::ProfileOutput> stream/*std::vector<std::vector<dna::profile>>& profiles*/)
{

	mixer::Mixer mxr;
	generator::ProfileGenerator pg;

	std::list<std::list<dna::genotypeSet>> markers;

	dna::profile::const_iterator it = profileToRemove.begin();
	std::for_each(mixture.begin(), mixture.end(), [&markers, &it, this, &threads](const dna::marker& marker){
		std::vector<dna::allel> desiredpro;
		desiredpro.push_back(it->first);
		desiredpro.push_back(it->second);

		std::list<dna::genotypeSet> marset;
		UnifyMixRemoveMarker(marker, desiredpro, threads, marset);
		//set with removed profiles

		if (marset.empty())
		{
			//FIXME what about it?
			std::cout << "MARKERS EMPTY!!!\n";
		}
		markers.push_back(marset);
		++it;
	});


	// generating
	if (markers.empty())
		std::cout << "MARKERS EMPTY!!!\n";
	pg.generateAll(markers, stream, howManyPeople-1);
	return true;
}

bool ui::UInt::UnifyAndMixMarker(const dna::marker& marker, int threads,
		std::list<dna::genotypeSet>& results)
{
	ThreadPool pool(threads);

	std::mutex resultGuard;
	ar::utils::MarkerUnifier mu(howManyPeople);

	BOOST_FOREACH(dna::marker mark, mu.unify(marker))
	{
	threads::MixTask task(mark, results, resultGuard);
	pool.run_task<threads::MixTask>(task);
	}

	pool.wait();

	return true;
}

bool ui::UInt::UnifyMixRemoveMarker(const dna::marker& marker,
		const std::vector<dna::allel>& profiles, int threads,
		std::list<dna::genotypeSet>& results)
{
	// std::cout<<"In remove int\n";
	ThreadPool pool(threads);

	std::mutex resultGuard;

	ar::utils::MarkerUnifier mu(howManyPeople);

	BOOST_FOREACH(dna::marker mark, mu.unify(marker)){
	bool canexist=true;

	std::vector<dna::allel> profi=profiles;
	dna::marker marks=mark;
	std::for_each(profi.begin(), profi.end(), [&canexist, &marks](dna::allel& allel){
		dna::marker::iterator it=std::find(marks.begin(), marks.end(),allel);
		if(it!=marks.end())
		{
			marks.erase(it);
		}
		else
		{
			canexist=false;
		}
	});

	if(canexist==true)
	{
		threads::MixTask task(marks, results, resultGuard);
		pool.run_task<threads::MixTask>(task);
	}
}

	pool.wait();

	return true;
}

	//CheckCoverage- by RK
	//Check the condition that for each marker the following condition must be met:
	//for mixture of 2 people: number of alleli in mixture marker- number of unique alleli in given profiles<=2
	//so that the generated profile will have at most 2 unique alleli 
	//for mixture of 3 people: number of alleli in mixture marker- number of unique alleli in given profiles<=4	
	//so that the 2 generated profiles will have at most 4 unique alleli 	
bool ui::UInt::checkCoverage(const std::vector<dna::marker>& mixture,
		const dna::profile& profile, int howmany)
{
//FIXME- To be corrected

	//the vector unique alleli extracted
	std::vector<dna::allel> unique;

	dna::profile::const_iterator pit = profile.begin();
	for (unsigned int i = 0; i != mixture.size(); ++i)
	{

			if (std::find(unique.begin(), unique.end(), pit->first)== unique.end())
				unique.push_back(pit->first);
			if (std::find(unique.begin(), unique.end(), pit->second)== unique.end())
				unique.push_back(pit->second);
		if (howmany == 2)
		{
		//number of alleli in mixture marker- number of unique alleli in given profiles<=2
			if (unique.size() + 2 < mixture[i].size())
				return false;
		}
		if (howmany == 3)
		{
		//number of alleli in mixture marker- number of unique alleli in given profiles<=4
			if (unique.size() + 4 < mixture[i].size())
				return false;
		}
	++pit;	
	}
	return true;
}
